# 导包
# 引入自动化工具 用于创建浏览器驱动对象，从而针对web软件进行测试
from selenium import webdriver
# 引入 页面元素选择工具 只有选择上了才能对其进行操作
from selenium.webdriver.common.by import By
# 引入时间歌工具
import time
# 导入鼠标操作工具包
from selenium.webdriver.common.action_chains import ActionChains
# 导包键盘操作包：
from selenium.webdriver.common.keys import Keys
# 导入元素显示等待工具包
from selenium.webdriver.support.wait import WebDriverWait



# 为何需要进行元素操作
# 1. 需要让脚本模拟用户给指定元素输入值
# 2. 需要让脚本模拟人为删除元素的内容
# 3. 需要让脚本模拟点击操作

# 元素常用操作方法
# 1. click() 单击元素
# 2. send_keys(value) 模拟输入
# 3. clear() 清除文本

# 案例 需求：打开注册A页面，完成以下操作
# 1).通过脚本执行输入用户名：admin；密码：123456；电话号码：18611111111；电子邮件：123@qq.com
# driver=webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
# # 选择元素 填写数据
# driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("admin");
# driver.find_element(By.CSS_SELECTOR,"[name='passwordA']").send_keys("123456");
# driver.find_element(By.CSS_SELECTOR,".telA").send_keys("18611111111");
# driver.find_element(By.CSS_SELECTOR,"input[name='emailA']").send_keys("123@qq.com")
# # 2).间隔3秒，修改电话号码为：18600000000
# time.sleep(3)
# driver.find_element(By.CSS_SELECTOR,".telA").clear();
# driver.find_element(By.CSS_SELECTOR,".telA").send_keys("18600000000")
# # 3).间隔3秒，点击‘注册’按钮
# time.sleep(3)
# driver.find_element(By.CSS_SELECTOR,"button").click();
# # 4).间隔3秒，关闭浏览器
# time.sleep(3)
# driver.close();
# # 5).元素定位方法不限



# 浏览器操作  通过调用selenium API 实现  下面是常用命令
#1. maximize_window() 最大化浏览器窗口 --> 模拟浏览器最大化按钮
# 2. set_window_size(width, height) 设置浏览器窗口大小 --> 设置浏览器宽、高(像素点)
# driver=webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
# driver.set_window_size(1350,650);
# # 3. set_window_position(x, y) 设置浏览器窗口位置 --> 设置浏览器位置
# driver.set_window_position(30,200);
# 4. back() 后退 --> 模拟浏览器后退按钮
# driver.find_element(By.LINK_TEXT,"百度").click();
# time.sleep(3)
# driver.back()
# 5. forward() 前进 --> 模拟浏览器前进按钮
# 6. refresh() 刷新 --> 模拟浏览器F5刷新
# driver.find_element(By.LINK_TEXT,"百度").click();
# time.sleep(3)
# driver.refresh();

# 7. close() 关闭当前窗口 --> 模拟点击浏览器关闭按钮
# 8. quit() 关闭浏览器驱动对象 --> 关闭所有程序启动的窗口
# 9. title 获取页面title
# print(driver.title);
# 10. current_url 获取当前页面URL
# print(driver.current_url)



# 获取元素信息
# 为什么要学习获取元素信息的方法？
# 1. 如何获取元素的文本？
# 2. 如何获取元素属性值？
# 3. 如何让程序判断元素是否为可见状态？
# 我们想解决以上问题，就需要学习Selenium封装的获取元素信息的方法

# 1. size 返回元素大小
# 2. text 获取元素的文本
# 3. get_attribute("xxx") 获取属性值，传递的参数为元素的属性名
# 4. is_displayed() 判断元素是否可见
# 5. is_enabled() 判断元素是否可用
# 6. is_selected() 判断元素是否选中，用来检查复选框或单选按钮是否被选中

# 需求：使用‘注册A.html’页面，完成以下操作：
# 1).获取用户名输入框的大小
# driver=webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
# print(driver.find_element(By.CSS_SELECTOR,"#userA").size);
# # 2).获取页面上第一个超链接的文本内容
# print(driver.find_element(By.LINK_TEXT,"百度").text);
# # 3).获取页面上第一个超链接的地址
# print(driver.find_element(By.LINK_TEXT,"百度").get_attribute("href"))
# # 4).判断页面中的span标签是否可见
# print(driver.find_element(By.CSS_SELECTOR,"span").is_displayed())
# # 5).判断页面中取消按钮是否可用
# print(driver.find_element(By.CSS_SELECTOR,"#cancelA").is_enabled())
# # 6).判断页面中'旅游'对应的复选框是否为选中的状态
# print(driver.find_element(By.CSS_SELECTOR,"#yyA").is_selected())


# 
# 鼠标操作 现在Web产品中存在丰富的鼠标交互方式，作为一个Web自动化测试框架，需要应对这些鼠标操
# 作的应用场景
# 说明：在Selenium中将操作鼠标的方法封装在ActionChains类中
#from selenium.webdriver.common.action_chains import ActionChains
# 实例化对象：
# action = ActionChains(driver)
# 方法：
# 1. context_click(element) 右击 --> 模拟鼠标右键点击效果
# driver=webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
# # 创建鼠标操作对象
# action = ActionChains(driver)
# # # 打开注册页面A，在用户名文本框上点击鼠标右键
# w=driver.find_element(By.CSS_SELECTOR,"#userA")
# action.context_click(w) ;
# action.perform()
# 三合一 action.context_click(driver.find_element(By.CSS_SELECTOR,"#userA")).perform();
# 2. double_click(element) 双击 --> 模拟鼠标双击效果
# action.double_click(driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("sasa")).perform()
# 3. drag_and_drop(source, target) 拖动 --> 模拟鼠标拖动效果    action.drag_and_drop_by_offset() 随意设置拖到位置 参数 移动物品，X,Y坐标方向数值

# 4. move_to_element(element) 悬停 --> 模拟鼠标悬停效果
# 需求：打开注册页面A，模拟鼠标悬停在‘注册’按钮上
# action.move_to_element(driver.find_element(By.CSS_SELECTOR,"button")).perform();
# 5. perform() 执行 --> 此方法用来执行以上所有鼠标操作
# 为了更好的学习其他方法，我们先学习perform()执行方法,因为所有的方法都需要执行才能生效




# 键盘操作
#1）.模拟键盘上一些按键或者组合键的输入 如：Ctrl+C 、Ctrl+V；2). Selenium中把键盘的按键都封装在Keys类中
# 1. send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
# 2. send_keys(Keys.SPACE) 空格键(Space)
# 3. send_keys(Keys.TAB) 制表键(Tab)
# 4. send_keys(Keys.ESCAPE) 回退键(Esc)
# 5. send_keys(Keys.ENTER) 回车键(Enter)
# 6. send_keys(Keys.CONTROL,'a') 全选(Ctrl+A) 粘贴 将a改为V即可 一切使用CTRL都这样写即可
# 7. send_keys(Keys.CONTROL,'c') 复制(Ctrl+C)

# 需求：打开注册A页面，完成以下操作
# 1). 输入用户名：admin1，暂停2秒，删除1
# driver=webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
# name=driver.find_element(By.CSS_SELECTOR,"input[name='userA']")
# pas=driver.find_element(By.CSS_SELECTOR,"input[name='passwordA']")
# name.send_keys("admin") #填写数据
# # 2). 全选用户名：admin，暂停2秒 键盘形式
# name.send_keys(Keys.CONTROL,'a');
# time.sleep(2)
# # 3). 复制用户名：admin，暂停2秒
# name.send_keys(Keys.CONTROL,'c');
# time.sleep(2)
# # 4). 粘贴到密码框
# pas.send_keys(Keys.CONTROL,'v');


#  元素等待 :定位页面元素时如果未找到，会在指定时间内一直等待的过程
# 为什么要设置元素等待？
# 1. 网络速度慢
# 2. 电脑配置低
# 3. 服务器处理请求慢
# 元素等待类型 :
# 1.隐式等待
# 概念：定位元素时，如果能定位到元素则直接返回该元素，不触发等待； 如果不能定位到该元
# 素，则间隔一段时间后再去定位元素； 如果在达到最大时长时还没有找到指定元素，则抛出元素
# 不存在的异常 NoSuchElementException

# 使用方法  driver.implicitly_wait(timeout)  # (timeout：为等待最大时长，单位：秒
# 说明：隐式等待为全局设置（只需要设置一次，就会作用于所有元素）
# driver = webdriver.Chrome();
# driver.get("http://121.43.169.97:8848/pageA.html");
#设置全局 元素等待时间  1秒
# driver.implicitly_wait(1)
# driver.find_element(By.CSS_SELECTOR,"input[name='userB']").send_keys("ad")
# 2. 显式等待
# 概念：定位指定元素时，如果能定位到元素则直接返回该元素，不触发等待； 如果不能定位到该
# 元素，则间隔一段时间后再去定位元素； 如果在达到最大时长时还没有找到指定元素，则抛出超
# 时异常 TimeoutException
# 在Selenium中把显式等待的相关方法封装在WebDriverWait类中 from selenium.webdriver.support.wait import WebDriverWait
# 使用方法：
# WebDriverWait(driver, timeout, poll_frequency=0.5)
# 1). driver：浏览器驱动对象
# 2). timeout：超时的时长，单位：秒
# 3). poll_frequency：检测间隔时间，默认为0.5秒
# 3. 调用方法 until(method)：直到...时
# 1). method：函数名称，该函数用来实现对元素的定位
# 2). 一般使用匿名函数来实现：lambda x: x.find_element_by_id("userA")
# 4. element = WebDriverWait(driver, 10, 1).until(lambda x: x.find_element_by_id("userA")
# )
# 需求：打开注册A页面，完成以下操作
# 1).使用显式等待定位用户名输入框，如果元素存在，就输入admin
# 注意他会在超时时间内一直查找，所以在所操作的元素中进行添加数据的话会一直执行，超过时间未找到则报超时错误
# name=WebDriverWait(driver,5,0.5).until(lambda x:x.find_element(By.CSS_SELECTOR,"input[name='userA']"));
# name.send_keys("ad");
# 错误例子
# name=WebDriverWait(driver,5,0.5).until(lambda x:x.find_element(By.CSS_SELECTOR,"input[name='userB']"));
# name.send_keys("ad");


# 显式与隐式区别
# 1. 作用域：隐式为全局元素，显式等待为单个元素有效
# 2. 使用方法：隐式等待直接通过驱动对象调用，而显式等待方法封装在WebDriverWait类中
# 3. 达到最大超时时长后抛出的异常不同：隐式为NoSuchElementException，显式等待为TimeoutExcepti



# frame切换
# frame：HTML页面中的一种框架，主要作用是在当前页面中指定区域显示另一页面元素； 下面是html构建例子
# 形式一：[了解]
# <frameset cols="25%,75%">
    # <frame src="frame_a.htm">
    # <frame src="frame_b.htm">
# </frameset>
# 形式二：
# <iframe name="iframe_a" src="demo_iframe.htm" width="200" height="200"></iframe>

# 为什么要学习frame切换  当主页面有 frame 元素构建 页面时需要使用针对于此的方法
# 1.2 frame切换方法
# 说明：在Selenium中封装了如何切换frame框架的方法
# 方法：
# 1). driver.switch_to.frame(frame_reference) --> 切换到指定frame的方法
# frame_reference：可以为frame框架的name、id或者定位到的frame元素  取值为frame在html中的书写方式中的name或  id 看上面
# 2). driver.switch_to.default_content() --> 恢复默认页面方法
# 在frame中操作其他页面，必须先回到默认页面，才能进一步操作
# 因为根据所在层级，主界面包容两个frame，frame中 是同级关系，所以需要通过同一个父级（上级）进行互通
# 所以才会有恢复默认页面再转到其他frame中的方法
# 当然也要根据实际的frame层级关系进行使用，如frame嵌套的话，猜测就不许要退回主界面了，直接切

# 案例：打开‘注册实例.html’页面，完成以下操作
# driver=webdriver.Chrome();
# driver.get("file:///D:/CShi/CS_xm/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
# # 1). 填写主页面的注册信息
# driver.find_element(By.CSS_SELECTOR,"#user").send_keys("admin")
# # 2). 填写注册页面A中的注册信息
# driver.switch_to.frame("myframe1"); # 只举一个例子
# driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("qyhA")
# # 3). 填写注册页面B中的注册信息
# driver.switch_to.default_content()
# driver.switch_to.frame("myframe2"); # 只举一个例子
# driver.find_element(By.CSS_SELECTOR,"#userB").send_keys("qyhB")





# 滚动条操作  种可控制页面显示范围的组件
# 在HTML页面中，由于前端技术框架的原因，页面元素为动态显示，元素根据滚动条的下拉而被加载
# 如： 页面注册同意条款，需要滚动条到最底层，才能点击同意
# 由于selenium中并没有直接提供操作滚动条的方法，但是它提供了可执行JavaScript脚本方法，可通过js进行操作滚动条

# 1. 设置JavaScript脚本控制滚动条
# js = "window.scrollTo(0,1000)"
# (0:左边距；1000：上边距；单位像素)
# 2. selenium调用执行JavaScript脚本的方法
# driver.execute_script(js)

# 需求：打开注册页面A，暂停2秒后，滚动条拉到最底层
# 构建浏览器驱动对象，通过驱动启动项目，完成案例
# driver=webdriver.Chrome();
# # 启动项目 只拿到老师给的网页文件，所以就存在本地，进行使用了
# driver.get("file:///D:/CShi/CS_xm/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE/%E6%B3%A8%E5%86%8CA.html");
# time.sleep(2);
# js="window.scrollTo(0,1000)" # X轴 ，Y轴
# # 执行滚动条拉到最底层操作
# driver.execute_script(js)




# 多窗口切换
# 说明：在HTML页面中，当点击超链接或者按钮时，有的会在新的窗口打开页，当想要操作新界面的元素时需要 多窗口切换方法
# 超链接跳转或者其他跳转方式，表面上看是跳转了，但我们的操作窗口还是在未跳转时的窗口中，所以无法操控其他窗口元素

# 如何实现多窗口切换
# 说明：在Selenium中封装了获取当前窗口句柄、获取所有窗口句柄和切换到指定句柄窗口的方法；
# 句柄：英文handle，窗口的唯一识别码
# 方法：
# 1). driver.current_window_handle --> 获取当前窗口句柄
# 2). driver.window_handles --> 获取所有窗口句柄
# 3). driver.switch_to.window(handle) --> 切换指定句柄窗口

# 需求：打开‘注册实例.html’页面，完成以下操作
# driver=webdriver.Chrome();
# driver.get("file:///D:/CShi/CS_xm/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
# zhu=driver.current_window_handle;
# print(f"当前窗口是：{zhu}")
# # 1). 点击‘注册A页面’链接
# driver.find_element(By.LINK_TEXT,"注册A网页").click();
# # 获取所有窗口句柄
# hea=driver.window_handles;
# # 2). 在打开的页面中，填写注册信息
# for i in hea:
#     if i != zhu:
#         # 当窗口不是主窗口时，就表示他是一个新的（超链接跳转窗口）窗口，所以进行指定，添加数据
#         driver.switch_to.window(i);
#         driver.find_element(By.CSS_SELECTOR, "#userA").send_keys("一条转成都")
#
# time.sleep(4);



# 窗口截图
# 说明：把当前操作的页面，截图保存到指定位置
# 自动化在执行出错的时候，对当前窗口截图保存，那么通过图片就可以非常直观地看到出错的原因
#说明：在Selenium中，提供了截图方法，我们只需要调用即可
# 方法：
# driver.get_screenshot_as_file(imgpath)
# imgpath：图片保存路径
#
# # 需求：打开‘注册A.html’页面，完成以下操作
# # 1). 填写注册信息
# driver=webdriver.Chrome();
# driver.get("file:///D:/CShi/CS_xm/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE/%E6%B3%A8%E5%86%8CA.html")
# # 通过 CSS id名称 进行元素选择  (他们都有id 就统一此形式 进行元素选择)
# driver.find_element(By.CSS_SELECTOR,"#userA").send_keys("qsh");
# driver.find_element(By.CSS_SELECTOR,"#passwordA").send_keys("132456");
# driver.find_element(By.CSS_SELECTOR,"#telA").send_keys("15576676677");
# driver.find_element(By.CSS_SELECTOR,"#emailA").send_keys("277@qq.com");
# time.sleep(3)
# # 2). 截图保存  将时间戳作为文件名称
# # driver.get_screenshot_as_file("./img/admin.png");
# driver.get_screenshot_as_file("./img/%s.png" %(time.strftime("%Y_%m_%d_%H_%M_%S")));


 #  验证码
#  说明：一种随机生成的信息（数字、字母、汉字、图片、算术题）等为了防止恶意的请求行为，增加应用的安全性
# 验证码的处理方式:记录cookie(去掉验证码、设置万能验证码：都是开发来完成,识别验证码过于复杂识别度不高不采用)
#
#Selenium操作cookie
# 说明：Selenium中对cookie操作提供相应的方法
# 方法：
# 1. get_cookie(name) --> 获取指定cookie
# name:为cookie的名称
# 2. get_cookies() --> 获取本网站所有本地cookies
# 3. add_cookie(cookie_dict) --> 添加cookie
# cookie_dict：一个字典对象，必选的键包括："name" and "value"


# 需求：使用cookie实现跳过登录
# 1). 手动登录百度，获取cookie
# 构建浏览器驱动.打开百度位置，手动获取cookie
driver=webdriver.Chrome();
driver.get("https://www.baidu.com/");
# 2). 使用获取到的cookie，达到登录目的，然后就可以执行登录之后的操作
# DUSS是登录百度后的唯一身份凭证(*.baidu.com)，拿到BDUSS就等于拿到帐号的控制权，
driver.add_cookie({'name':'BDUSS',"value":"ZJTWVUWk51aUd5LUpuR0RBeFFidmotNTl3MVdsbDNmdUNxRjVSckxNcGxBaXhwSVFBQUFBJCQAAAAAAQAAAAEAAAAUSTAoQW5ud2l0aGx1Y2t5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGV1BGlldQRpTm"});
driver.refresh()
